package com.redispro.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.redispro.dto.*;
import com.redispro.entity.User;
import com.redispro.mapper.UserMapper;
import com.redispro.service.*;
import com.redispro.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 李力
 * @since 2022-12-22
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    //发送验证码
    @Override
    public Result sendCode(String phone) {
        //1.校验手机号码
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2.不符合，返回错误信息
            return Result.fail("无效的手机号");
        }
        //3.符合，生成六位的验证码
        String code = RandomUtil.randomNumbers(6);
        //4.保存验证码到redis并且设置有限期
        redisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code, Duration.ofMinutes(1));
        //5.发送验证码到手机,这个就打印下就行
        log.info("发送验证码为:" + code);
        //6.返回
        return Result.ok();
    }

    //短信验证或者注册
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result login(LoginFormDTO loginForm) {
        //1.校验手机号
        if (RegexUtils.isPhoneInvalid(loginForm.getPhone())) {
            //1.1不符合，返回错误信息
            return Result.fail("无效的手机号");
        }
        //2.检验验证码
        String cacheCode = redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + loginForm.getPhone());
        String code = loginForm.getCode();
        if (code == null || !code.equals(cacheCode)) {
            //2.1不通过
            return Result.fail("验证码过期或者错误");
        }
        //2.2验证码通过也有一个清除验证码的操作，释放缓存
        redisTemplate.delete(RedisConstants.LOGIN_CODE_KEY + loginForm.getPhone());
        //3.判断用户是否存在
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone, loginForm.getPhone());
        User user = getOne(wrapper);
        if (user == null) {
            //不存在，创建用户并保存到数据库
            user = new User();
            user.setPhone(loginForm.getPhone());
            user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(5));
            save(user);
        }
        //4.生成token
        String token = UUID.randomUUID().toString(true);
        //5.将用户信息存入redis
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        redisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY + token, BeanUtil.beanToMap(userDTO
                , new HashMap<>(), CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((k, v) -> v.toString())
        ));
        //设置有限期
        redisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, Duration.ofMinutes(30));
        //6.返回
        return Result.ok(token);
    }

    @Override
    public Result sign() {
        Long currentUserId = UserHolder.getUser().getId();
        //1.获取日期
        LocalDateTime now = LocalDateTime.now();
        //2.拼接key
        String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM"));
        String key = "sign" + ":" + currentUserId + ":" + format;
        //3.获取今天是本月的第几天,从1开始
        int dayOfMonth = now.getDayOfMonth();
        //4.签到
        redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        //5.返回
        return Result.ok();
    }

    @Override
    public Result signCount() {
        Long userId = UserHolder.getUser().getId();
        //1.获取日期
        LocalDateTime now = LocalDateTime.now();
        //2.拼接key
        String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM"));
        String key = "sign" + ":" + userId + ":" + format;
        //3.获取今天是本月的第几天,从1开始
        int dayOfMonth = now.getDayOfMonth();
        //4.获取本月截至今天为止的所有签到记录，返回的是一个十进制的数字
        List<Long> longs = redisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().
                        get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).
                        valueAt(0)
        );
        if (longs == null || longs.isEmpty()) {
            return Result.ok(0);
        }
        Long aLong = longs.get(0);
        if (aLong == null || aLong == 0) {
            return Result.ok(0);
        }
        //5.循环遍历
        int count = 0;
        while (true) {
            //6.与数字1做与运算，得到数字的最后一个bit位
            if ((aLong & 1) == 0) {
                //未签到，结束
                break;
            } else {
                //签到，计数器加1
                count++;
            }
            aLong = aLong >>> 1;
        }
        return Result.ok(count);
    }
}
